//-------------------------------------------------------------------
//	Copyright (c) 2013  Zhirnov Andrey
//	This file is part of the "Noise Library" project.
//	See copyright notice in "linNoiseCL.h".
//-------------------------------------------------------------------

#pragma once

#include "../CSource.h"

namespace Noise
{

	//
	// Math Function Source with 2 arguments
	//

	template <typename SrcFunc, ESource SourceType>
	class CMathSource2Args : public CSource
	{
	// types
	protected:
		typedef CMathSource2Args< SrcFunc, SourceType >	Self;


	// variables
	protected:
		CSourcePtr	_left,
					_right;
		
		static const
			ESource	_sourceType = SourceType;


	// methods
	public:
		CMathSource2Args ()
		{}

		~CMathSource2Args ()
		{}

		ESourceState GetState () const
		{
			return (_left.IsNotNull() and _right.IsNotNull()) ? SOURCE_COMPLETE : UNCOMPLETE_ATTACHMENT_SOURCE;
		}
		
		virtual void Delete ()
		{
			_left  = null;
			_right = null;
		}

		virtual EResult GetInteger (ESourceQuery type, int *v, int count) const
		{
			CHECK_ARGUMENT_R( count >= 1, INVALID_VALUE );

			switch ( type )
			{
				case SOURCE_STATE :
					v[0] = GetState();
					return OK;

				case SOURCE_TYPE :
					v[0] = _sourceType;
					return OK;

				case SOURCES_COUNT :
				{
					int	left_count  = 0,
						right_count = 0;

					_left->GetInteger( SOURCES_COUNT, &left_count, 1 );
					_right->GetInteger( SOURCES_COUNT, &right_count, 1 );

					v[0] = left_count + right_count + 1;
					return OK;
				}

				case INPUT_SOURCES :
					v[0] = 2;
					return OK;
			}
			return INVALID_ENUM;
		}

		virtual EResult SetArg (EArgument type, CSourcePtr src)
		{
			switch ( type )
			{
				case SOURCE_0 :	_left = src;	return OK;
				case SOURCE_1 :	_right = src;	return OK;
			}
			return INVALID_ENUM;
		}

		virtual EResult GetArg (EArgument type, CSourcePtr &src) const
		{
			switch ( type )
			{
				case SOURCE_0 :	src = _left;	return OK;
				case SOURCE_1 :	src = _right;	return OK;
			}
			return INVALID_ENUM;
		}
		
		virtual CSourcePtr Clone () const override
		{
			return new Self();
		}
		
		virtual void PreBuild (const TPreBuildData &data) override
		{
			data.Push( _left.ptr() );
			data.Push( _right.ptr() );
		}

		virtual bool BuildFunction (TFunctionData &data) override
		{
			CSource::BuildFunctionHeader( data.source, _id ) << " ( ";
			SrcFunc::Build( data, _left->GetSourceId(), _right->GetSourceId() );
			data.source << " )\n";
			return true;
		}
		
		NATIVE(
		template < typename Real, Real (CSource::*NativeFunction)(const TVec<Real,3> &) >
		Real NativeFunction (const TVec<Real,3> &vPoint)
		{
			return SrcFunc::Native< Real, NativeFunction >( vPoint, _left, _right );
		}

		virtual float32 NativeFunction32 (const fvec3 &vPoint) override
		{
			return NativeFunction< float32, &CSource::NativeFunction32 >( vPoint );
		}

		virtual float64 NativeFunction64 (const dvec3 &vPoint) override
		{
			return NativeFunction< float64, &CSource::NativeFunction64 >( vPoint );
		})

		static bool CreateSource (CSourcePtr &src, CContext *context, ESource type)
		{
			ASSERT( type == _sourceType );

			src = new Self();
			return true;
		}
	};



	//
	// Add
	//

	struct Math_Add
	{
		static void Build (CSource::TFunctionData &data, uint src0, uint src1)
		{
			CSource::BuildFunctionCall( data.source, src0 );
			data.source << " + ";
			CSource::BuildFunctionCall( data.source, src1 );
		}
		
		NATIVE(
		template < typename Real, Real (CSource::*NativeFunction)(const TVec<Real,3> &) >
		static Real Native (const TVec<Real,3> &vPoint, CSourcePtr left, CSourcePtr right)
		{
			return (left.ptr()->*NativeFunction)( vPoint ) + (right.ptr()->*NativeFunction)( vPoint );
		})
	};

	typedef CMathSource2Args< Math_Add, ADD >	CSourceAdd;



	//
	// Sub
	//

	struct Math_Sub
	{
		static void Build (CSource::TFunctionData &data, uint src0, uint src1)
		{
			CSource::BuildFunctionCall( data.source, src0 );
			data.source << " - ";
			CSource::BuildFunctionCall( data.source, src1 );
		}
		
		template < typename Real, Real (CSource::*NativeFunction)(const TVec<Real,3> &) >
		static Real Native (const TVec<Real,3> &vPoint, CSourcePtr left, CSourcePtr right)
		{
			return (left.ptr()->*NativeFunction)( vPoint ) - (right.ptr()->*NativeFunction)( vPoint );
		}
	};

	typedef CMathSource2Args< Math_Sub, SUB >	CSourceSub;



	//
	// Div
	//

	struct Math_Div
	{
		static void Build (CSource::TFunctionData &data, uint src0, uint src1)
		{
			CSource::BuildFunctionCall( data.source, src0 );
			data.source << " / ";
			CSource::BuildFunctionCall( data.source, src1 );
		}
		
		NATIVE(
		template < typename Real, Real (CSource::*NativeFunction)(const TVec<Real,3> &) >
		static Real Native (const TVec<Real,3> &vPoint, CSourcePtr left, CSourcePtr right)
		{
			return (left.ptr()->*NativeFunction)( vPoint ) / (right.ptr()->*NativeFunction)( vPoint );
		})
	};

	typedef CMathSource2Args< Math_Div, DIV >	CSourceDiv;



	//
	// Max
	//

	struct Math_Max
	{
		static void Build (CSource::TFunctionData &data, uint src0, uint src1)
		{
			data.source << "max( ";
			CSource::BuildFunctionCall( data.source, src0 );
			data.source << ", ";
			CSource::BuildFunctionCall( data.source, src1 );
			data.source << " )";
		}
		
		NATIVE(
		template < typename Real, Real (CSource::*NativeFunction)(const TVec<Real,3> &) >
		static Real Native (const TVec<Real,3> &vPoint, CSourcePtr left, CSourcePtr right)
		{
			return Max( (left.ptr()->*NativeFunction)( vPoint ), (right.ptr()->*NativeFunction)( vPoint ) );
		})
	};

	typedef CMathSource2Args< Math_Max, MAX >	CSourceMax;



	//
	// Min
	//

	struct Math_Min
	{
		static void Build (CSource::TFunctionData &data, uint src0, uint src1)
		{
			data.source << "min( ";
			CSource::BuildFunctionCall( data.source, src0 );
			data.source << ", ";
			CSource::BuildFunctionCall( data.source, src1 );
			data.source << " )";
		}
		
		NATIVE(
		template < typename Real, Real (CSource::*NativeFunction)(const TVec<Real,3> &) >
		static Real Native (const TVec<Real,3> &vPoint, CSourcePtr left, CSourcePtr right)
		{
			return Min( (left.ptr()->*NativeFunction)( vPoint ), (right.ptr()->*NativeFunction)( vPoint ) );
		})
	};

	typedef CMathSource2Args< Math_Min, MIN >	CSourceMin;



	//
	// Mod
	//

	struct Math_Mod
	{
		static void Build (CSource::TFunctionData &data, uint src0, uint src1)
		{
			data.source << "fmod( ";
			CSource::BuildFunctionCall( data.source, src0 );
			data.source << ", ";
			CSource::BuildFunctionCall( data.source, src1 );
			data.source << " )";
		}
		
		NATIVE(
		template < typename Real, Real (CSource::*NativeFunction)(const TVec<Real,3> &) >
		static Real Native (const TVec<Real,3> &vPoint, CSourcePtr left, CSourcePtr right)
		{
			return Mod( (left.ptr()->*NativeFunction)( vPoint ), (right.ptr()->*NativeFunction)( vPoint ) );
		})
	};

	typedef CMathSource2Args< Math_Mod, MOD >	CSourceMod;



	//
	// Mul
	//

	struct Math_Mul
	{
		static void Build (CSource::TFunctionData &data, uint src0, uint src1)
		{
			CSource::BuildFunctionCall( data.source, src0 );
			data.source << " * ";
			CSource::BuildFunctionCall( data.source, src1 );
		}
		
		NATIVE(
		template < typename Real, Real (CSource::*NativeFunction)(const TVec<Real,3> &) >
		static Real Native (const TVec<Real,3> &vPoint, CSourcePtr left, CSourcePtr right)
		{
			return (left.ptr()->*NativeFunction)( vPoint ) * (right.ptr()->*NativeFunction)( vPoint );
		})
	};

	typedef CMathSource2Args< Math_Mul, MUL >	CSourceMul;



	//
	// Normalized Exp
	//

	struct Math_NormExp
	{
		static void Build (CSource::TFunctionData &data, uint src0, uint src1)
		{
			data.source << " ( pow( fabs(( ";
			CSource::BuildFunctionCall( data.source, src0 );
			data.source << " + 1.0 ) * 0.5), ";
			CSource::BuildFunctionCall( data.source, src1 );
			data.source << " ) * 2.0 - 1.0 )";
		}
		
		NATIVE(
		template < typename Real, Real (CSource::*NativeFunction)(const TVec<Real,3> &) >
		static Real Native (const TVec<Real,3> &vPoint, CSourcePtr left, CSourcePtr right)
		{
			Real	v0 = (left.ptr()->*NativeFunction)( vPoint );
			Real	v1 = (right.ptr()->*NativeFunction)( vPoint );

			return Pow( Abs( ( v0 + Real(1.0) ) * Real(0.5) ), v1 ) * Real(2.0) - Real(1.0);
		})
	};

	typedef CMathSource2Args< Math_NormExp, NORM_EXP >	CSourceNormExp;



	//
	// Pow
	//
	
	struct Math_Pow
	{
		static void Build (CSource::TFunctionData &data, uint src0, uint src1)
		{
			data.source << "pow( ";
			CSource::BuildFunctionCall( data.source, src0 );
			data.source << ", ";
			CSource::BuildFunctionCall( data.source, src1 );
			data.source << " )";
		}
		
		NATIVE(
		template < typename Real, Real (CSource::*NativeFunction)(const TVec<Real,3> &) >
		static Real Native (const TVec<Real,3> &vPoint, CSourcePtr left, CSourcePtr right)
		{
			return Pow( (left.ptr()->*NativeFunction)( vPoint ), (right.ptr()->*NativeFunction)( vPoint ) );
		})
	};

	typedef CMathSource2Args< Math_Pow, POW >	CSourcePow;


}	// Noise